package org.hk.proxy.server.handlers;

import cn.hutool.core.util.StrUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOption;
import io.netty.channel.SimpleChannelInboundHandler;
import org.hk.proxy.common.constant.CommonKey;
import org.hk.proxy.protocol.ProxyMessage;
import org.hk.proxy.server.channel.ProxyChannelManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.concurrent.*;

/**
 *  用户请求的handler
 */
public class UserChannelHandler extends SimpleChannelInboundHandler<ByteBuf> {
    private static final Logger log = LoggerFactory.getLogger(UserChannelHandler.class);
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
        Channel userChannel = ctx.channel();
        Channel proxyChannel = userChannel.attr(CommonKey.PROXY_USER).get();
        byte[] bytes = new byte[msg.readableBytes()];
        msg.readBytes(bytes);
        if (proxyChannel == null) {
            // 该端口还没有代理客户端
            log.error("proxyChannel is null ,being closed:{}",userChannel);
            ctx.channel().close();
        }else{
            /**
             * 如果客户端的channel已经建立，则推送消息到客户端
             */
            ProxyMessage proxyMessage = new ProxyMessage();
            proxyMessage.setType(ProxyMessage.P_TYPE_TRANSFER);
            proxyMessage.setSerialNumber(userChannel.attr(CommonKey.SID).get());
            proxyMessage.setData(bytes);
            proxyChannel.writeAndFlush(proxyMessage);
        }
    }
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception{
        log.info("user channel active:{}",ctx.channel());
        Channel userChannel = ctx.channel();
        InetSocketAddress sa = (InetSocketAddress) userChannel.localAddress();
        int port = sa.getPort();
        Channel cmdProxy = ProxyChannelManager.selectChannelByPort(port);
        if(cmdProxy==null) {
            log.error("cmdChannel is null.closed");
            ctx.channel().close();
            return;
        }
        log.info("cmdProxyChannel status is {}",cmdProxy.isActive());
        ProxyMessage proxyMessage = new ProxyMessage();
        proxyMessage.setType(ProxyMessage.TYPE_CONNECT);
        String lanInfo = ProxyChannelManager.getLanInfoByPort(port);
        if(StrUtil.isBlank(lanInfo)){
            ctx.channel().close();
            return;
        }else {
            userChannel.config().setOption(ChannelOption.AUTO_READ, false);
            proxyMessage.setUri(lanInfo);
            Long sId = ProxyChannelManager.addUserChannel(userChannel);
            userChannel.attr(CommonKey.SID).set(sId);
            proxyMessage.setSerialNumber(sId);
            cmdProxy.writeAndFlush(proxyMessage);
        }
        super.channelActive(ctx);
    }
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Channel proxyChannel = ctx.channel().attr(CommonKey.PROXY_USER).getAndSet(null);
        if(proxyChannel==null){
            ctx.channel().close();
        }else {
            proxyChannel.attr(CommonKey.PROXY_USER).set(null);
            proxyChannel.config().setOption(ChannelOption.AUTO_READ, true);
            ProxyMessage msg = new ProxyMessage();
            msg.setType(ProxyMessage.TYPE_DISCONNECT);
            proxyChannel.writeAndFlush(msg);
        }
        super.channelInactive(ctx);
    }
    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        super.channelUnregistered(ctx);
    }

    @Override
    public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
        Channel userChannel = ctx.channel();
        InetSocketAddress sa = (InetSocketAddress) userChannel.localAddress();
        Channel cmdChannel = ProxyChannelManager.selectChannelByPort(sa.getPort());
        if(cmdChannel==null){
            ctx.channel().close();
        }else {
            Channel proxyChannel = userChannel.attr(CommonKey.PROXY_USER).get();
            if(proxyChannel!=null){
                proxyChannel.config().setOption(ChannelOption.AUTO_READ,userChannel.isWritable());
            }
        }
        super.channelWritabilityChanged(ctx);
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        if(cause instanceof IOException){
            channelInactive(ctx);
        }else {
            super.exceptionCaught(ctx,cause);
        }
    }
}